﻿using System.Linq;

using Microsoft.WowAddonStudio.Lua.Model;

using NUnit.Framework;

namespace Microsoft.WowAddonStudio.Lua.Tests.Model
{
    [TestFixture]
    public class ModelTest
    {
        private Documentation documentation;

        [SetUp]
        public void SetUp()
        {
             documentation = new Documentation();
        }

        [Test]
        public void FunctionTest()
        {
            var assertFunction = new Function
                                 {
                                     Name = "assert",
                                     Summary =
                                         "Asserts a value evaluates to true. If it is, returns value, otherwise causes a Lua error to be thrown.",
                                     Parameters = new[]
                                                  {
                                                      new Parameter
                                                      {
                                                          Name = "value",
                                                          Summary = "The value to be evaluated."
                                                      }, new Parameter
                                                         {
                                                             Name = "errormsg",
                                                             Summary = "The message of the error to be thrown.",
                                                             Type = LuaType.String,
                                                             Optional = true
                                                         }
                                                  },
                                 };

            var unitExistsFunction = new Function
                                     {
                                         Name = "UnitExists",
                                         Summary = "Returns whether or not a given unit exists.",
                                         Parameters = new[]
                                                      {
                                                          new ParameterChoice
                                                          {
                                                              Choices = new[]
                                                                        {
                                                                            new Parameter
                                                                            {
                                                                                Name = "unit",
                                                                                Summary = "The unit to query",
                                                                                ApiType = ApiType.UnitId
                                                                            }, new Parameter
                                                                               {
                                                                                   Name = "name",
                                                                                   Summary =
                                                                                       "The full name of a unit to query",
                                                                                   Type = LuaType.String
                                                                               }
                                                                        }
                                                          }
                                                      },
                                         Returns = new[]
                                                   {
                                                       new ReturnValue
                                                       {
                                                           Name = "exists",
                                                           Summary = "1 if the unit exists, nil otherwise",
                                                           ApiType = ApiType.OneNil
                                                       }
                                                   }
                                     };

            documentation.AddRange(new[]
                                   {
                                       assertFunction, unitExistsFunction
                                   });

            Assert.AreEqual(LuaType.Function, assertFunction.Type);
            Assert.AreEqual(LuaType.Function, unitExistsFunction.Type);
            Assert.AreEqual(2, documentation.Declarations.Count);
        }

        [Test]
        public void TableTest()
        {
            var stringTable = new Table
                              {
                                  Name = "string",
                                  Declarations =
                                      {
                                          new Function
                                          {
                                              Name = "format",
                                              Summary = "Returns a formatted string using the values passed in.",
                                              Parameters = new[]
                                                           {
                                                               new Parameter
                                                               {
                                                                   Name = "formatString",
                                                                   Summary = "The format of the string.",
                                                                   Type = LuaType.String
                                                               }, new Parameter
                                                                  {
                                                                      Name = "value",
                                                                      Summary = "The values to be used in the string."
                                                                  }
                                                           },
                                              Variadic = true,
                                              Returns = new[]
                                                        {
                                                            new ReturnValue
                                                            {
                                                                Name = "formattedString",
                                                                Summary = "The formatted string.",
                                                                Type = LuaType.String
                                                            }
                                                        }
                                          }
                                      }
                              };

            documentation.Add(stringTable);
        }

        [Test]
        public void TablePrototypeTest()
        {
            // Create virtual table that will act as a prototype to another table
            var objectTable = new Table
                                 {
                                     Name = "Object",
                                     Virtual = true,
                                     Declarations =
                                         {
                                             new Function
                                             {
                                                 Name = "GetType",
                                                 Summary = "Returns the type of the object.",
                                                 Returns = new[]
                                                           {
                                                               new ReturnValue
                                                               {
                                                                   Name = "type",
                                                                   Summary = "The type of the object.",
                                                                   Type = LuaType.Table
                                                               }
                                                            }
                                             },

                                             new Function
                                             {
                                                 Name = "Equals",
                                                 Summary = "Determines whether two objects are equal.",
                                                 Parameters = new[]
                                                              {
                                                                  new Parameter
                                                                  {
                                                                      Name = "other",
                                                                      Summary = "The other object.",
                                                                      Type = LuaType.Table
                                                                  }
                                                              },
                                                 Returns = new[]
                                                           {
                                                               new ReturnValue
                                                               {
                                                                   Name = "equal",
                                                                   Summary = "A value indicatnig whether the two objects are equal.",
                                                                   Type = LuaType.Boolean
                                                               }
                                                            }
                                             },
                                         }
                                 };

            // Add virtual table to documentation
            documentation.Add(objectTable);

            // Create table that uses a prototype
            var fooTable = new Table
                           {
                               Name = "Foo",
                               Prototype = "Object",
                               Declarations =
                                   {
                                       new Variable
                                       {
                                           Name = "Bar",
                                           Type = LuaType.Number
                                       }
                                   }
                           };

            documentation.Add(fooTable);

            // Assert that there are two declarations and that fooTable has three declarations,
            // two functions and a variable

            Assert.AreEqual(2, documentation.Declarations.Count);
            Assert.AreEqual(3, fooTable.Declarations.Count);

            Assert.AreEqual(2, fooTable.Declarations.OfType<Function>().Count());
            Assert.AreEqual(1, fooTable.Declarations.OfType<Variable>().Count());
        }

        [Test]
        public void VariableTest()
        {
            var variable = new Variable
                           {
                               Name = "i",
                               Type = LuaType.Number,
                               Summary = "Iterator variable."
                           };

            documentation.Add(variable);
        }
    }
}